Biome: fixes for compontents directory

This commit is contained in:
Utkarsh-Patel-13 2025-07-27 10:41:15 -07:00
parent 758603b275
commit 2950573271
69 changed files with 478 additions and 648 deletions

View file

@ -1,5 +1,4 @@
"use client";
import { cn } from "@/lib/utils";
import {
IconBrandDiscord,
IconBrandGithub,
@ -8,6 +7,7 @@ import {
} from "@tabler/icons-react";
import Link from "next/link";
import type React from "react";
import { cn } from "@/lib/utils";
export function Footer() {
const pages = [
@ -32,8 +32,8 @@ export function Footer() {
</div>
<ul className="transition-colors flex sm:flex-row flex-col hover:text-text-neutral-800 text-neutral-600 dark:text-neutral-300 list-none gap-4">
{pages.map((page, idx) => (
<li key={"pages" + idx} className="list-none">
{pages.map((page) => (
<li key={`pages-${page.title}`} className="list-none">
<Link className="transition-colors hover:text-text-neutral-800" href={page.href}>
{page.title}
</Link>

View file

@ -1,7 +1,7 @@
"use client";
import Link from "next/link";
import React from "react";
import Image from "next/image";
import Link from "next/link";
import { cn } from "@/lib/utils";
export const Logo = ({ className }: { className?: string }) => {

View file

@ -1,9 +1,7 @@
"use client";
import { cn } from "@/lib/utils";
import { IconFileTypeDoc, IconBrandGithub, IconBrandDiscord } from "@tabler/icons-react";
import { IconBrandDiscord, IconBrandGithub, IconFileTypeDoc } from "@tabler/icons-react";
import Link from "next/link";
import React from "react";
import { motion } from "framer-motion";
import { cn } from "@/lib/utils";
import { Logo } from "./Logo";
export function ModernHeroWithGradients() {
@ -88,6 +86,7 @@ const TopLines = () => {
xmlns="http://www.w3.org/2000/svg"
className="aspect-square pointer-events-none absolute inset-x-0 top-0 h-[100px] w-full md:h-[200px]"
>
<title>Top Lines</title>
<line
y1="-0.5"
x2="406"
@ -212,6 +211,7 @@ const BottomLines = () => {
xmlns="http://www.w3.org/2000/svg"
className="aspect-square pointer-events-none absolute inset-x-0 -bottom-20 z-20 h-[150px] w-full md:h-[300px]"
>
<title>Bottom Lines</title>
<line x1="139.5" y1="418" x2="139.5" y2="12" stroke="url(#paint0_linear_0_1)" />
<line x1="172.5" y1="418" x2="172.5" y2="12" stroke="url(#paint1_linear_0_1)" />
<line x1="205.5" y1="418" x2="205.5" y2="12" stroke="url(#paint2_linear_0_1)" />
@ -344,6 +344,7 @@ const SideLines = () => {
xmlns="http://www.w3.org/2000/svg"
className="pointer-events-none absolute inset-0 z-30 h-full w-full"
>
<title>Side Lines</title>
<path
d="M268 115L181.106 6.97176C178.069 3.19599 173.485 1 168.639 1H0"
stroke="url(#paint0_linear_337_46)"
@ -451,6 +452,7 @@ const BottomGradient = ({ className }: { className?: string }) => {
className
)}
>
<title>Bottom Gradient</title>
<path
d="M118.499 0H532.468L635.375 38.6161L665 194.625L562.093 346H0L24.9473 121.254L118.499 0Z"
fill="url(#paint0_radial_254_132)"
@ -487,6 +489,7 @@ const TopGradient = ({ className }: { className?: string }) => {
className
)}
>
<title>Top Gradient</title>
<path
d="M807 110.119L699.5 -117.546L8.5 -154L-141 246.994L-7 952L127 782.111L279 652.114L513 453.337L807 110.119Z"
fill="url(#paint0_radial_254_135)"
@ -527,7 +530,7 @@ const TopGradient = ({ className }: { className?: string }) => {
);
};
const DarkModeGradient = ({ className }: { className?: string } = {}) => {
const DarkModeGradient = () => {
return (
<div className="hidden dark:block">
<div className="absolute -left-48 -top-48 h-[800px] w-[800px] rounded-full bg-purple-900/20 blur-[180px]"></div>

View file

@ -1,13 +1,12 @@
"use client";
import { cn } from "@/lib/utils";
import { IconMenu2, IconX, IconBrandGoogleFilled, IconUser } from "@tabler/icons-react";
import { motion, AnimatePresence, useScroll, useMotionValueEvent } from "framer-motion";
import { IconMenu2, IconUser, IconX } from "@tabler/icons-react";
import { AnimatePresence, motion, useMotionValueEvent, useScroll } from "framer-motion";
import Link from "next/link";
import type React from "react";
import { useRef, useState } from "react";
import { Button } from "./ui/button";
import { cn } from "@/lib/utils";
import { Logo } from "./Logo";
import { ThemeTogglerComponent } from "./theme/theme-toggle";
import { Button } from "./ui/button";
interface NavbarProps {
navItems: {
@ -109,7 +108,7 @@ const DesktopNav = ({ navItems, visible }: NavbarProps) => {
>
{navItems.map((navItem, idx) => (
<motion.div
key={`nav-item-${idx}`}
key={`nav-item-${navItem.name}`}
onHoverStart={() => setHoveredIndex(idx)}
className="relative"
>
@ -194,96 +193,94 @@ const MobileNav = ({ navItems, visible }: NavbarProps) => {
};
return (
<>
<motion.div
animate={{
backdropFilter: "blur(16px)",
background: visible
? "rgba(var(--background-rgb), 0.8)"
: "rgba(var(--background-rgb), 0.6)",
width: visible ? "80%" : "90%",
y: visible ? 0 : 8,
borderRadius: open ? "24px" : "full",
padding: "8px 16px",
}}
initial={{
width: "80%",
background: "rgba(var(--background-rgb), 0.6)",
}}
transition={{
type: "spring",
stiffness: 400,
damping: 30,
}}
className={cn(
"flex relative flex-col lg:hidden w-full justify-between items-center max-w-[calc(100vw-2rem)] mx-auto z-50 backdrop-saturate-[1.8] rounded-full",
visible ? "border border-solid dark:border-white/40 border-gray-300/30" : "border-0"
)}
style={
{
"--background-rgb": "var(--tw-dark) ? '0, 0, 0' : '255, 255, 255'",
} as React.CSSProperties
}
>
<div className="flex flex-row justify-between items-center w-full">
<Logo className="h-8 w-8 rounded-md" />
<div className="flex items-center gap-2">
<ThemeTogglerComponent />
{open ? (
<IconX className="dark:text-white/90 text-gray-800" onClick={() => setOpen(!open)} />
) : (
<IconMenu2
className="dark:text-white/90 text-gray-800"
onClick={() => setOpen(!open)}
/>
)}
</div>
</div>
<AnimatePresence>
{open && (
<motion.div
initial={{
opacity: 0,
y: -20,
}}
animate={{
opacity: 1,
y: 0,
}}
exit={{
opacity: 0,
y: -20,
}}
transition={{
type: "spring",
stiffness: 400,
damping: 30,
}}
className="flex rounded-3xl absolute top-16 dark:bg-black/80 bg-white/90 backdrop-blur-xl backdrop-saturate-[1.8] inset-x-0 z-50 flex-col items-start justify-start gap-4 w-full px-6 py-8"
>
{navItems.map((navItem: { link: string; name: string }, idx: number) => (
<Link
key={`link=${idx}`}
href={navItem.link}
onClick={() => setOpen(false)}
className="relative dark:text-white/90 text-gray-800 hover:text-gray-900 dark:hover:text-white transition-colors"
>
<motion.span className="block">{navItem.name}</motion.span>
</Link>
))}
<Button
onClick={handleGoogleLogin}
variant="outline"
className="flex cursor-pointer items-center gap-2 mt-4 w-full justify-center rounded-full dark:bg-white/20 dark:hover:bg-white/30 dark:text-white bg-gray-100 hover:bg-gray-200 text-gray-800 border-0"
>
<IconUser className="h-4 w-4" />
<span>Sign in</span>
</Button>
</motion.div>
<motion.div
animate={{
backdropFilter: "blur(16px)",
background: visible
? "rgba(var(--background-rgb), 0.8)"
: "rgba(var(--background-rgb), 0.6)",
width: visible ? "80%" : "90%",
y: visible ? 0 : 8,
borderRadius: open ? "24px" : "full",
padding: "8px 16px",
}}
initial={{
width: "80%",
background: "rgba(var(--background-rgb), 0.6)",
}}
transition={{
type: "spring",
stiffness: 400,
damping: 30,
}}
className={cn(
"flex relative flex-col lg:hidden w-full justify-between items-center max-w-[calc(100vw-2rem)] mx-auto z-50 backdrop-saturate-[1.8] rounded-full",
visible ? "border border-solid dark:border-white/40 border-gray-300/30" : "border-0"
)}
style={
{
"--background-rgb": "var(--tw-dark) ? '0, 0, 0' : '255, 255, 255'",
} as React.CSSProperties
}
>
<div className="flex flex-row justify-between items-center w-full">
<Logo className="h-8 w-8 rounded-md" />
<div className="flex items-center gap-2">
<ThemeTogglerComponent />
{open ? (
<IconX className="dark:text-white/90 text-gray-800" onClick={() => setOpen(!open)} />
) : (
<IconMenu2
className="dark:text-white/90 text-gray-800"
onClick={() => setOpen(!open)}
/>
)}
</AnimatePresence>
</motion.div>
</>
</div>
</div>
<AnimatePresence>
{open && (
<motion.div
initial={{
opacity: 0,
y: -20,
}}
animate={{
opacity: 1,
y: 0,
}}
exit={{
opacity: 0,
y: -20,
}}
transition={{
type: "spring",
stiffness: 400,
damping: 30,
}}
className="flex rounded-3xl absolute top-16 dark:bg-black/80 bg-white/90 backdrop-blur-xl backdrop-saturate-[1.8] inset-x-0 z-50 flex-col items-start justify-start gap-4 w-full px-6 py-8"
>
{navItems.map((navItem: { link: string; name: string }) => (
<Link
key={`link-${navItem.name}`}
href={navItem.link}
onClick={() => setOpen(false)}
className="relative dark:text-white/90 text-gray-800 hover:text-gray-900 dark:hover:text-white transition-colors"
>
<motion.span className="block">{navItem.name}</motion.span>
</Link>
))}
<Button
onClick={handleGoogleLogin}
variant="outline"
className="flex cursor-pointer items-center gap-2 mt-4 w-full justify-center rounded-full dark:bg-white/20 dark:hover:bg-white/30 dark:text-white bg-gray-100 hover:bg-gray-200 text-gray-800 border-0"
>
<IconUser className="h-4 w-4" />
<span>Sign in</span>
</Button>
</motion.div>
)}
</AnimatePresence>
</motion.div>
);
};

View file

@ -1,7 +1,7 @@
"use client";
import { useEffect } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { useEffect } from "react";
interface TokenHandlerProps {
redirectPath?: string; // Path to redirect after storing token

View file

@ -1,8 +1,9 @@
"use client";
import { BadgeCheck, ChevronsUpDown, LogOut, Settings } from "lucide-react";
import { BadgeCheck, LogOut, Settings } from "lucide-react";
import { useRouter } from "next/navigation";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
@ -12,8 +13,6 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
import { useRouter, useParams } from "next/navigation";
export function UserDropdown({
user,

View file

@ -1,11 +1,11 @@
"use client";
import { cn } from "@/lib/utils";
import { Manrope } from "next/font/google";
import React, { useRef, useEffect, useReducer, useMemo } from "react";
import { RoughNotation, RoughNotationGroup } from "react-rough-notation";
import { useInView } from "framer-motion";
import { Manrope } from "next/font/google";
import { useEffect, useMemo, useReducer, useRef } from "react";
import { RoughNotation, RoughNotationGroup } from "react-rough-notation";
import { useSidebar } from "@/components/ui/sidebar";
import { cn } from "@/lib/utils";
// Font configuration - could be moved to a global font config file
const manrope = Manrope({
@ -115,7 +115,7 @@ export function AnimatedEmptyState() {
}, TIMING.SIDEBAR_TRANSITION);
return () => clearTimeout(stabilizeTimer);
}, [sidebarState]);
}, []);
// Handle highlight visibility based on layout stability and viewport visibility
useEffect(() => {

View file

@ -1,13 +1,14 @@
"use client";
import type React from "react";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { ExternalLink } from "lucide-react";
import type React from "react";
import { Button } from "@/components/ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
export const CitationDisplay: React.FC<{ index: number; node: any }> = ({ index, node }) => {
const truncateText = (text: string, maxLength: number = 200) => {
if (text.length <= maxLength) return text;
return text.substring(0, maxLength) + "...";
return `${text.substring(0, maxLength)}...`;
};
const handleUrlClick = (e: React.MouseEvent, url: string) => {
@ -26,13 +27,15 @@ export const CitationDisplay: React.FC<{ index: number; node: any }> = ({ index,
<PopoverContent className="w-80 p-4 space-y-3 relative" align="start">
{/* External Link Button - Top Right */}
{node?.url && (
<button
<Button
size="icon"
variant="ghost"
onClick={(e) => handleUrlClick(e, node.url)}
className="absolute top-3 right-3 inline-flex items-center justify-center w-6 h-6 text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-200 hover:bg-blue-50 dark:hover:bg-blue-900/20 rounded transition-colors"
title="Open in new tab"
>
<ExternalLink size={14} />
</button>
</Button>
)}
{/* Heading */}

View file

@ -1,7 +1,7 @@
"use client";
import { SuggestedQuestions } from "@llamaindex/chat-ui/widgets";
import { getAnnotationData, type Message, useChatUI } from "@llamaindex/chat-ui";
import { SuggestedQuestions } from "@llamaindex/chat-ui/widgets";
import {
Accordion,
AccordionContent,
@ -14,7 +14,7 @@ export const ChatFurtherQuestions: React.FC<{ message: Message }> = ({ message }
const { append, requestData } = useChatUI();
if (annotations.length !== 1 || annotations[0].length === 0) {
return <></>;
return null;
}
return (

View file

@ -1,15 +1,24 @@
"use client";
import { ChatInput } from "@llamaindex/chat-ui";
import { FolderOpen, Check, Zap, Brain } from "lucide-react";
import { Brain, Check, FolderOpen, Zap } from "lucide-react";
import { useParams } from "next/navigation";
import React, { Suspense, useCallback, useState } from "react";
import type { ResearchMode } from "@/components/chat";
import {
ConnectorButton as ConnectorButtonComponent,
getConnectorIcon,
} from "@/components/chat/ConnectorComponents";
import { DocumentsDataTable } from "@/components/chat/DocumentsDataTable";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogTitle,
DialogTrigger,
DialogFooter,
} from "@/components/ui/dialog";
import {
Select,
@ -18,19 +27,9 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Badge } from "@/components/ui/badge";
import { Suspense, useState, useCallback } from "react";
import { useParams } from "next/navigation";
import { useDocuments, type Document } from "@/hooks/use-documents";
import { DocumentsDataTable } from "@/components/chat/DocumentsDataTable";
import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors";
import {
getConnectorIcon,
ConnectorButton as ConnectorButtonComponent,
} from "@/components/chat/ConnectorComponents";
import type { ResearchMode } from "@/components/chat";
import { type Document, useDocuments } from "@/hooks/use-documents";
import { useLLMConfigs, useLLMPreferences } from "@/hooks/use-llm-configs";
import React from "react";
import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors";
const DocumentSelector = React.memo(
({
@ -188,24 +187,17 @@ const ConnectorSelector = React.memo(
const isSelected = selectedConnectors.includes(connector.type);
return (
<div
<Button
key={connector.id}
className={`flex items-center gap-2 p-2 rounded-md border cursor-pointer transition-colors ${
isSelected
? "border-primary bg-primary/10"
: "border-border hover:border-primary/50 hover:bg-muted"
}`}
className={`flex items-center gap-2 p-2 rounded-md border cursor-pointer transition-colors`}
onClick={() => handleConnectorToggle(connector.type)}
role="checkbox"
aria-checked={isSelected}
tabIndex={0}
variant={isSelected ? "default" : "outline"}
size="sm"
type="button"
>
<div className="flex-shrink-0 w-6 h-6 flex items-center justify-center rounded-full bg-muted">
{getConnectorIcon(connector.type)}
</div>
{getConnectorIcon(connector.type)}
<span className="flex-1 text-sm font-medium">{connector.name}</span>
{isSelected && <Check className="h-4 w-4 text-primary" />}
</div>
</Button>
);
})
)}

View file

@ -1,11 +1,10 @@
"use client";
import React from "react";
import { ChatSection as LlamaIndexChatSection, type ChatHandler } from "@llamaindex/chat-ui";
import type { Document } from "@/hooks/use-documents";
import { ChatInputUI } from "@/components/chat/ChatInputGroup";
import { type ChatHandler, ChatSection as LlamaIndexChatSection } from "@llamaindex/chat-ui";
import type { ResearchMode } from "@/components/chat";
import { ChatInputUI } from "@/components/chat/ChatInputGroup";
import { ChatMessagesUI } from "@/components/chat/ChatMessages";
import type { Document } from "@/hooks/use-documents";
interface ChatInterfaceProps {
handler: ChatHandler;

View file

@ -1,17 +1,17 @@
"use client";
import React from "react";
import {
ChatMessage as LlamaIndexChatMessage,
ChatMessages as LlamaIndexChatMessages,
type Message,
useChatUI,
} from "@llamaindex/chat-ui";
import TerminalDisplay from "@/components/chat/ChatTerminal";
import ChatSourcesDisplay from "@/components/chat/ChatSources";
import { useEffect, useRef } from "react";
import { AnimatedEmptyState } from "@/components/chat/AnimatedEmptyState";
import { CitationDisplay } from "@/components/chat/ChatCitation";
import { ChatFurtherQuestions } from "@/components/chat/ChatFurtherQuestions";
import { AnimatedEmptyState } from "@/components/chat/AnimatedEmptyState";
import ChatSourcesDisplay from "@/components/chat/ChatSources";
import TerminalDisplay from "@/components/chat/ChatTerminal";
import { languageRenderers } from "@/components/chat/CodeBlock";
export function ChatMessagesUI() {
@ -37,13 +37,13 @@ export function ChatMessagesUI() {
}
function ChatMessageUI({ message, isLast }: { message: Message; isLast: boolean }) {
const bottomRef = React.useRef<HTMLDivElement>(null);
const bottomRef = useRef<HTMLDivElement>(null);
React.useEffect(() => {
useEffect(() => {
if (isLast && bottomRef.current) {
bottomRef.current.scrollIntoView({ behavior: "smooth" });
}
}, [message]);
}, [isLast]);
return (
<LlamaIndexChatMessage message={message} isLast={isLast} className="flex flex-col ">

View file

@ -1,8 +1,12 @@
"use client";
import { useState } from "react";
import { getAnnotationData, type Message } from "@llamaindex/chat-ui";
import { IconBrandGithub } from "@tabler/icons-react";
import { ExternalLink, FileText, Globe } from "lucide-react";
import { useState } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import {
Dialog,
DialogContent,
@ -11,10 +15,6 @@ import {
DialogTrigger,
} from "@/components/ui/dialog";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { ExternalLink, FileText, Globe } from "lucide-react";
import { IconBrandGithub } from "@tabler/icons-react";
interface Source {
id: string;
@ -44,10 +44,6 @@ interface SourceNode {
metadata: NodeMetadata;
}
interface NodesResponse {
nodes: SourceNode[];
}
function getSourceIcon(type: string) {
switch (type) {
case "USER_SELECTED_GITHUB_CONNECTOR":

View file

@ -1,16 +1,26 @@
"use client";
import React from "react";
import { getAnnotationData, type Message } from "@llamaindex/chat-ui";
import { useEffect, useRef, useState } from "react";
import { Button } from "@/components/ui/button";
export default function TerminalDisplay({ message, open }: { message: Message; open: boolean }) {
const [isCollapsed, setIsCollapsed] = React.useState(!open);
const [isCollapsed, setIsCollapsed] = useState(!open);
const bottomRef = React.useRef<HTMLDivElement>(null);
const bottomRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (bottomRef.current) {
bottomRef.current.scrollTo({
top: bottomRef.current.scrollHeight,
behavior: "smooth",
});
}
}, []);
// Get the last assistant message that's not being typed
if (!message) {
return <></>;
return null;
}
interface TerminalInfo {
@ -22,24 +32,17 @@ export default function TerminalDisplay({ message, open }: { message: Message; o
const events = getAnnotationData(message, "TERMINAL_INFO") as TerminalInfo[];
if (events.length === 0) {
return <></>;
return null;
}
React.useEffect(() => {
if (bottomRef.current) {
bottomRef.current.scrollTo({
top: bottomRef.current.scrollHeight,
behavior: "smooth",
});
}
}, [events]);
return (
<div className="bg-gray-900 rounded-lg border border-gray-700 overflow-hidden font-mono text-sm shadow-lg">
{/* Terminal Header */}
<div
className="bg-gray-800 px-4 py-2 flex items-center gap-2 border-b border-gray-700 cursor-pointer hover:bg-gray-750 transition-colors"
<Button
className="w-full bg-gray-800 px-4 py-2 flex items-center gap-2 border-b border-gray-700 cursor-pointer hover:bg-gray-750 transition-colors"
onClick={() => setIsCollapsed(!isCollapsed)}
variant="ghost"
type="button"
>
<div className="flex gap-2">
<div className="w-3 h-3 rounded-full bg-red-500"></div>
@ -52,6 +55,7 @@ export default function TerminalDisplay({ message, open }: { message: Message; o
<div className="text-gray-400">
{isCollapsed ? (
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<title>Collapse</title>
<path
strokeLinecap="round"
strokeLinejoin="round"
@ -61,6 +65,7 @@ export default function TerminalDisplay({ message, open }: { message: Message; o
</svg>
) : (
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<title>Expand</title>
<path
strokeLinecap="round"
strokeLinejoin="round"
@ -70,7 +75,7 @@ export default function TerminalDisplay({ message, open }: { message: Message; o
</svg>
)}
</div>
</div>
</Button>
{/* Terminal Content */}
{!isCollapsed && (

View file

@ -1,5 +1,5 @@
import React, { useState } from "react";
import { ExternalLink } from "lucide-react";
import { memo, useState } from "react";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import {
@ -20,57 +20,55 @@ type CitationProps = {
/**
* Citation component to handle individual citations
*/
export const Citation = React.memo(
({ citationId, citationText, position, source }: CitationProps) => {
const [open, setOpen] = useState(false);
const citationKey = `citation-${citationId}-${position}`;
export const Citation = memo(({ citationId, citationText, position, source }: CitationProps) => {
const [open, setOpen] = useState(false);
const citationKey = `citation-${citationId}-${position}`;
if (!source) return <>{citationText}</>;
if (!source) return <>{citationText}</>;
return (
<span key={citationKey} className="relative inline-flex items-center">
<DropdownMenu open={open} onOpenChange={setOpen}>
<DropdownMenuTrigger asChild>
<sup>
<span className="inline-flex items-center justify-center text-primary cursor-pointer bg-primary/10 hover:bg-primary/15 w-4 h-4 rounded-full text-[10px] font-medium ml-0.5 transition-colors border border-primary/20 shadow-sm">
{citationId}
</span>
</sup>
</DropdownMenuTrigger>
{open && (
<DropdownMenuContent align="start" className="w-80 p-0" forceMount>
<Card className="border-0 shadow-none">
<div className="p-3 flex items-start gap-3">
<div className="flex-shrink-0 w-7 h-7 flex items-center justify-center bg-muted rounded-full">
{getConnectorIcon(source.connectorType || "")}
</div>
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<h3 className="font-medium text-sm text-card-foreground">{source.title}</h3>
</div>
<p className="text-sm text-muted-foreground mt-0.5">{source.description}</p>
<div className="mt-2 flex items-center text-xs text-muted-foreground">
<span className="truncate max-w-[200px]">{source.url}</span>
</div>
</div>
<Button
variant="ghost"
size="icon"
className="h-7 w-7 rounded-full"
onClick={() => window.open(source.url, "_blank", "noopener,noreferrer")}
title="Open in new tab"
>
<ExternalLink className="h-3.5 w-3.5" />
</Button>
return (
<span key={citationKey} className="relative inline-flex items-center">
<DropdownMenu open={open} onOpenChange={setOpen}>
<DropdownMenuTrigger asChild>
<sup>
<span className="inline-flex items-center justify-center text-primary cursor-pointer bg-primary/10 hover:bg-primary/15 w-4 h-4 rounded-full text-[10px] font-medium ml-0.5 transition-colors border border-primary/20 shadow-sm">
{citationId}
</span>
</sup>
</DropdownMenuTrigger>
{open && (
<DropdownMenuContent align="start" className="w-80 p-0" forceMount>
<Card className="border-0 shadow-none">
<div className="p-3 flex items-start gap-3">
<div className="flex-shrink-0 w-7 h-7 flex items-center justify-center bg-muted rounded-full">
{getConnectorIcon(source.connectorType || "")}
</div>
</Card>
</DropdownMenuContent>
)}
</DropdownMenu>
</span>
);
}
);
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<h3 className="font-medium text-sm text-card-foreground">{source.title}</h3>
</div>
<p className="text-sm text-muted-foreground mt-0.5">{source.description}</p>
<div className="mt-2 flex items-center text-xs text-muted-foreground">
<span className="truncate max-w-[200px]">{source.url}</span>
</div>
</div>
<Button
variant="ghost"
size="icon"
className="h-7 w-7 rounded-full"
onClick={() => window.open(source.url, "_blank", "noopener,noreferrer")}
title="Open in new tab"
>
<ExternalLink className="h-3.5 w-3.5" />
</Button>
</div>
</Card>
</DropdownMenuContent>
)}
</DropdownMenu>
</span>
);
});
Citation.displayName = "Citation";
@ -85,10 +83,10 @@ export const renderTextWithCitations = (
const citationRegex = /\[(\d+)\]/g;
const parts = [];
let lastIndex = 0;
let match;
let match: RegExpExecArray | null = citationRegex.exec(text);
let position = 0;
while ((match = citationRegex.exec(text)) !== null) {
while (match !== null) {
// Add text before the citation
if (match.index > lastIndex) {
parts.push(text.substring(lastIndex, match.index));
@ -108,6 +106,7 @@ export const renderTextWithCitations = (
lastIndex = match.index + match[0].length;
position++;
match = citationRegex.exec(text);
}
// Add any remaining text after the last citation

View file

@ -1,10 +1,10 @@
"use client";
import React, { useState, useEffect, useMemo, useCallback } from "react";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneLight, oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
import { Check, Copy } from "lucide-react";
import { useTheme } from "next-themes";
import { memo, useCallback, useEffect, useMemo, useState } from "react";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark, oneLight } from "react-syntax-highlighter/dist/cjs/styles/prism";
// Constants for styling and configuration
const COPY_TIMEOUT = 2000;
@ -41,14 +41,14 @@ interface CodeBlockProps {
language: string;
}
type LanguageRenderer = (props: { code: string }) => React.JSX.Element
type LanguageRenderer = (props: { code: string }) => React.JSX.Element;
interface SyntaxStyle {
[key: string]: React.CSSProperties;
}
// Memoized fallback component for SSR/hydration
const FallbackCodeBlock = React.memo(({ children }: { children: string }) => (
const FallbackCodeBlock = memo(({ children }: { children: string }) => (
<div className="bg-muted p-4 rounded-md">
<pre className="m-0 p-0 border-0">
<code className="text-xs font-mono border-0 leading-6">{children}</code>
@ -59,7 +59,7 @@ const FallbackCodeBlock = React.memo(({ children }: { children: string }) => (
FallbackCodeBlock.displayName = "FallbackCodeBlock";
// Code block component with syntax highlighting and copy functionality
export const CodeBlock = React.memo<CodeBlockProps>(({ children, language }) => {
export const CodeBlock = memo<CodeBlockProps>(({ children, language }) => {
const [copied, setCopied] = useState(false);
const { resolvedTheme, theme } = useTheme();
const [mounted, setMounted] = useState(false);

View file

@ -1,28 +1,28 @@
import type React from "react";
import {
ChevronDown,
Plus,
Search,
Globe,
Sparkles,
Microscope,
Telescope,
File,
Link,
Webhook,
MessageCircle,
FileText,
} from "lucide-react";
import {
IconBrandDiscord,
IconBrandGithub,
IconBrandNotion,
IconBrandSlack,
IconBrandYoutube,
IconBrandGithub,
IconLayoutKanban,
IconLinkPlus,
IconBrandDiscord,
IconTicket,
} from "@tabler/icons-react";
import {
ChevronDown,
File,
FileText,
Globe,
Link,
MessageCircle,
Microscope,
Plus,
Search,
Sparkles,
Telescope,
Webhook,
} from "lucide-react";
import type React from "react";
import { Button } from "@/components/ui/button";
import type { Connector, ResearchMode } from "./types";
@ -238,7 +238,7 @@ export const ResearchModeControl = ({ value, onChange }: ResearchModeControlProp
<div className="flex items-center gap-2">
{/* Main Q/A vs Report Toggle */}
<div className="flex h-8 rounded-md border border-border overflow-hidden">
<button
<Button
className={`flex h-full items-center gap-1 px-3 text-xs font-medium transition-colors whitespace-nowrap ${
isQnaMode
? "bg-primary text-primary-foreground"
@ -249,8 +249,8 @@ export const ResearchModeControl = ({ value, onChange }: ResearchModeControlProp
>
<MessageCircle className="h-3 w-3" />
<span>Q/A</span>
</button>
<button
</Button>
<Button
className={`flex h-full items-center gap-1 px-3 text-xs font-medium transition-colors whitespace-nowrap ${
isReportMode
? "bg-primary text-primary-foreground"
@ -261,14 +261,14 @@ export const ResearchModeControl = ({ value, onChange }: ResearchModeControlProp
>
<FileText className="h-3 w-3" />
<span>Report</span>
</button>
</Button>
</div>
{/* Report Sub-options (only show when in Report mode) */}
{isReportMode && (
<div className="flex h-8 rounded-md border border-border overflow-hidden">
{reportSubOptions.map((option) => (
<button
<Button
key={option.value}
className={`flex h-full items-center gap-1 px-2 text-xs font-medium transition-colors whitespace-nowrap ${
getCurrentReportMode() === option.value
@ -280,7 +280,7 @@ export const ResearchModeControl = ({ value, onChange }: ResearchModeControlProp
>
{option.icon}
<span>{option.label}</span>
</button>
</Button>
))}
</div>
)}

View file

@ -1,6 +1,5 @@
"use client";
import * as React from "react";
import {
type ColumnDef,
type ColumnFiltersState,
@ -14,9 +13,11 @@ import {
type VisibilityState,
} from "@tanstack/react-table";
import { ArrowUpDown, Calendar, FileText, Search } from "lucide-react";
import { useEffect, useMemo, useState } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
@ -24,7 +25,6 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Input } from "@/components/ui/input";
import {
Table,
TableBody,
@ -33,7 +33,6 @@ import {
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Badge } from "@/components/ui/badge";
import type { Document, DocumentType } from "@/hooks/use-documents";
interface DocumentsDataTableProps {
@ -206,13 +205,13 @@ export function DocumentsDataTable({
onDone,
initialSelectedDocuments = [],
}: DocumentsDataTableProps) {
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
const [documentTypeFilter, setDocumentTypeFilter] = React.useState<DocumentType | "ALL">("ALL");
const [sorting, setSorting] = useState<SortingState>([]);
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
const [documentTypeFilter, setDocumentTypeFilter] = useState<DocumentType | "ALL">("ALL");
// Memoize initial row selection to prevent infinite loops
const initialRowSelection = React.useMemo(() => {
const initialRowSelection = useMemo(() => {
if (!documents.length || !initialSelectedDocuments.length) return {};
const selection: Record<string, boolean> = {};
@ -222,24 +221,24 @@ export function DocumentsDataTable({
return selection;
}, [documents, initialSelectedDocuments]);
const [rowSelection, setRowSelection] = React.useState<Record<string, boolean>>({});
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({});
// Only update row selection when initialRowSelection actually changes and is not empty
React.useEffect(() => {
useEffect(() => {
const hasChanges = JSON.stringify(rowSelection) !== JSON.stringify(initialRowSelection);
if (hasChanges && Object.keys(initialRowSelection).length > 0) {
setRowSelection(initialRowSelection);
}
}, [initialRowSelection]);
}, [initialRowSelection, rowSelection]);
// Initialize row selection on mount
React.useEffect(() => {
useEffect(() => {
if (Object.keys(rowSelection).length === 0 && Object.keys(initialRowSelection).length > 0) {
setRowSelection(initialRowSelection);
}
}, []);
}, [initialRowSelection, rowSelection]);
const filteredDocuments = React.useMemo(() => {
const filteredDocuments = useMemo(() => {
if (documentTypeFilter === "ALL") return documents;
return documents.filter((doc) => doc.document_type === documentTypeFilter);
}, [documents, documentTypeFilter]);
@ -260,11 +259,11 @@ export function DocumentsDataTable({
state: { sorting, columnFilters, columnVisibility, rowSelection },
});
React.useEffect(() => {
useEffect(() => {
const selectedRows = table.getFilteredSelectedRowModel().rows;
const selectedDocuments = selectedRows.map((row) => row.original);
onSelectionChange(selectedDocuments);
}, [rowSelection, onSelectionChange, table]);
}, [onSelectionChange, table]);
const handleClearAll = () => setRowSelection({});

View file

@ -47,7 +47,7 @@ export const useScrollIndicators = (
// Add resize listener to update indicators when window size changes
window.addEventListener("resize", updateIndicators);
return () => window.removeEventListener("resize", updateIndicators);
}, []);
}, [updateIndicators]);
return updateIndicators;
};

View file

@ -1,4 +1,5 @@
import type React from "react";
import { Button } from "@/components/ui/button";
type SegmentedControlProps<T extends string> = {
value: T;
@ -21,7 +22,7 @@ function SegmentedControl<T extends string>({
return (
<div className="flex h-7 rounded-md border border-border overflow-hidden">
{options.map((option) => (
<button
<Button
key={option.value}
className={`flex h-full items-center gap-1 px-2 text-xs transition-colors ${
value === option.value ? "bg-primary text-primary-foreground" : "hover:bg-muted"
@ -31,7 +32,7 @@ function SegmentedControl<T extends string>({
>
{option.icon}
<span>{option.label}</span>
</button>
</Button>
))}
</div>
);

View file

@ -1,4 +1,4 @@
import type { Source, Connector } from "./types";
import type { Connector, Source } from "./types";
/**
* Function to get sources for the main view

View file

@ -1,8 +1,9 @@
// Export all components and utilities from the chat folder
export { default as SegmentedControl } from "./SegmentedControl";
export * from "./ConnectorComponents";
export * from "./Citation";
export * from "./SourceUtils";
export * from "./ScrollUtils";
export * from "./CodeBlock";
export * from "./ConnectorComponents";
export * from "./ScrollUtils";
export { default as SegmentedControl } from "./SegmentedControl";
export * from "./SourceUtils";
export * from "./types";

View file

@ -1,8 +1,8 @@
"use client";
import { useEffect, useRef, useState } from "react";
import type { RefObject } from "react";
import { Button } from "./ui/button";
import { Copy, CopyCheck } from "lucide-react";
import type { RefObject } from "react";
import { useEffect, useRef, useState } from "react";
import { Button } from "./ui/button";
export default function CopyButton({ ref }: { ref: RefObject<HTMLDivElement | null> }) {
const [copy, setCopy] = useState(false);

View file

@ -1,4 +1,7 @@
import { FileText } from "lucide-react";
import type React from "react";
import { MarkdownViewer } from "@/components/markdown-viewer";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
@ -6,9 +9,6 @@ import {
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { MarkdownViewer } from "@/components/markdown-viewer";
import { FileText } from "lucide-react";
interface DocumentViewerProps {
title: string;

View file

@ -1,6 +1,7 @@
import React from "react";
import { Skeleton } from "@/components/ui/skeleton";
"use client";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
export function EditConnectorLoadingSkeleton() {
return (

View file

@ -1,6 +1,7 @@
import React from "react";
"use client";
import type { Control } from "react-hook-form";
import { FormField, FormItem, FormLabel, FormControl, FormMessage } from "@/components/ui/form";
import { FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
// Assuming EditConnectorFormValues is defined elsewhere or passed as generic

View file

@ -1,19 +1,19 @@
import { CircleAlert, Edit, KeyRound, Loader2 } from "lucide-react";
import type React from "react";
import type { UseFormReturn } from "react-hook-form";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormControl,
FormDescription,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Skeleton } from "@/components/ui/skeleton";
import { Edit, KeyRound, Loader2, CircleAlert } from "lucide-react";
// Types needed from parent
interface GithubRepo {

View file

@ -1,15 +1,16 @@
import React from "react";
"use client";
import { KeyRound } from "lucide-react";
import type { Control } from "react-hook-form";
import {
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormControl,
FormDescription,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { KeyRound } from "lucide-react";
// Assuming EditConnectorFormValues is defined elsewhere or passed as generic
interface EditSimpleTokenFormProps {

View file

@ -1,4 +1,7 @@
import { FileJson } from "lucide-react";
import React from "react";
import { defaultStyles, JsonView } from "react-json-view-lite";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
@ -6,9 +9,6 @@ import {
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { FileJson } from "lucide-react";
import { JsonView, defaultStyles } from "react-json-view-lite";
import "react-json-view-lite/dist/index.css";
interface JsonMetadataViewerProps {

View file

@ -1,15 +1,17 @@
import React, { useMemo, useState, useEffect, useRef } from "react";
import { Check, Copy } from "lucide-react";
import Image from "next/image";
import { useTheme } from "next-themes";
import React, { useEffect, useMemo, useRef, useState } from "react";
import ReactMarkdown from "react-markdown";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark, oneLight } from "react-syntax-highlighter/dist/cjs/styles/prism";
import rehypeRaw from "rehype-raw";
import rehypeSanitize from "rehype-sanitize";
import remarkGfm from "remark-gfm";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { Citation } from "./chat/Citation";
import type { Source } from "./chat/types";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneLight, oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
import { Check, Copy } from "lucide-react";
import { useTheme } from "next-themes";
import CopyButton from "./copy-button";
interface MarkdownViewerProps {
@ -112,7 +114,13 @@ export function MarkdownViewer({
),
hr: ({ node, ...props }: any) => <hr className="my-4 border-muted" {...props} />,
img: ({ node, ...props }: any) => (
<img className="max-w-full h-auto my-4 rounded" {...props} />
<Image
className="max-w-full h-auto my-4 rounded"
alt="markdown image"
height={100}
width={100}
{...props}
/>
),
table: ({ node, ...props }: any) => (
<div className="overflow-x-auto my-4">
@ -186,7 +194,8 @@ const CodeBlock = ({ children, language }: { children: string; language: string
return (
<div className="relative my-4 group">
<div className="absolute right-2 top-2 z-10">
<button
<Button
variant="ghost"
onClick={handleCopy}
className="p-1.5 rounded-md bg-background/80 hover:bg-background border border-border flex items-center justify-center transition-colors"
aria-label="Copy code"
@ -196,7 +205,7 @@ const CodeBlock = ({ children, language }: { children: string; language: string
) : (
<Copy size={14} className="text-muted-foreground" />
)}
</button>
</Button>
</div>
{mounted ? (
<SyntaxHighlighter
@ -297,10 +306,10 @@ const processCitationsInText = (
const citationRegex = /\[(\d+)\]/g;
const parts: React.ReactNode[] = [];
let lastIndex = 0;
let match;
let match: RegExpExecArray | null = citationRegex.exec(text);
let position = 0;
while ((match = citationRegex.exec(text)) !== null) {
while (match !== null) {
// Add text before the citation
if (match.index > lastIndex) {
parts.push(text.substring(lastIndex, match.index));
@ -322,6 +331,7 @@ const processCitationsInText = (
lastIndex = match.index + match[0].length;
position++;
match = citationRegex.exec(text);
}
// Add any remaining text after the last citation

View file

@ -1,8 +1,11 @@
"use client";
import type React from "react";
import { useState } from "react";
import { motion } from "framer-motion";
import { AlertCircle, Bot, Plus, Trash2 } from "lucide-react";
import { useState } from "react";
import { toast } from "sonner";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
@ -14,12 +17,7 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Badge } from "@/components/ui/badge";
import { Plus, Trash2, Bot, AlertCircle } from "lucide-react";
import { useLLMConfigs, type CreateLLMConfig } from "@/hooks/use-llm-configs";
import { toast } from "sonner";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { type CreateLLMConfig, useLLMConfigs } from "@/hooks/use-llm-configs";
const LLM_PROVIDERS = [
{ value: "OPENAI", label: "OpenAI", example: "gpt-4o, gpt-4, gpt-3.5-turbo" },

View file

@ -1,8 +1,12 @@
"use client";
import React, { useState, useEffect } from "react";
import { motion } from "framer-motion";
import { AlertCircle, Bot, Brain, CheckCircle, Zap } from "lucide-react";
import { useEffect, useState } from "react";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import {
Select,
SelectContent,
@ -10,10 +14,7 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Badge } from "@/components/ui/badge";
import { Brain, Zap, Bot, AlertCircle, CheckCircle } from "lucide-react";
import { useLLMConfigs, useLLMPreferences } from "@/hooks/use-llm-configs";
import { Alert, AlertDescription } from "@/components/ui/alert";
const ROLE_DESCRIPTIONS = {
long_context: {
@ -163,7 +164,7 @@ export function AssignRolesStep({ onPreferencesUpdated }: AssignRolesStepProps)
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Assign LLM Configuration:</label>
<Label className="text-sm font-medium">Assign LLM Configuration:</Label>
<Select
value={currentAssignment?.toString() || ""}
onValueChange={(value) => handleRoleAssignment(`${key}_llm_id`, value)}
@ -224,7 +225,7 @@ export function AssignRolesStep({ onPreferencesUpdated }: AssignRolesStepProps)
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<span>Progress:</span>
<div className="flex gap-1">
{Object.keys(ROLE_DESCRIPTIONS).map((key, index) => (
{Object.keys(ROLE_DESCRIPTIONS).map((key, _index) => (
<div
key={key}
className={`w-2 h-2 rounded-full ${

View file

@ -1,10 +1,9 @@
"use client";
import React from "react";
import { motion } from "framer-motion";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { ArrowRight, Bot, Brain, CheckCircle, Sparkles, Zap } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { CheckCircle, Bot, Brain, Zap, Sparkles, ArrowRight } from "lucide-react";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { useLLMConfigs, useLLMPreferences } from "@/hooks/use-llm-configs";
const ROLE_ICONS = {

View file

@ -1,15 +1,12 @@
"use client";
import { useState } from "react";
import { motion } from "framer-motion";
import { cn } from "@/lib/utils";
import { zodResolver } from "@hookform/resolvers/zod";
import { motion, type Variants } from "framer-motion";
import { MoveLeftIcon, Plus, Search, Trash2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Separator } from "@/components/ui/separator";
import { Tilt } from "@/components/ui/tilt";
import { Spotlight } from "@/components/ui/spotlight";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { useForm } from "react-hook-form";
import * as z from "zod";
import {
AlertDialog,
AlertDialogAction,
@ -21,9 +18,7 @@ import {
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import * as z from "zod";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
@ -33,7 +28,11 @@ import {
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { useRouter } from "next/navigation";
import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
import { Spotlight } from "@/components/ui/spotlight";
import { Tilt } from "@/components/ui/tilt";
import { cn } from "@/lib/utils";
// Define the form schema with Zod
const searchSpaceFormSchema = z.object({
@ -97,7 +96,7 @@ export function SearchSpaceForm({
},
};
const itemVariants = {
const itemVariants: Variants = {
hidden: { y: 20, opacity: 0 },
visible: {
y: 0,
@ -128,7 +127,8 @@ export function SearchSpaceForm({
: "Create a new search space to organize your documents, chats, and podcasts."}
</p>
</div>
<button
<Button
variant="ghost"
className="group relative rounded-full p-3 bg-background/80 hover:bg-muted border border-border hover:border-primary/20 shadow-sm hover:shadow-md transition-all duration-200 backdrop-blur-sm"
onClick={() => {
router.push("/dashboard");
@ -139,7 +139,7 @@ export function SearchSpaceForm({
className="text-muted-foreground group-hover:text-foreground transition-colors duration-200"
/>
<div className="absolute inset-0 rounded-full bg-gradient-to-r from-blue-500/10 to-purple-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
</button>
</Button>
</motion.div>
<motion.div className="w-full" variants={itemVariants}>

View file

@ -1,8 +1,25 @@
"use client";
import React, { useState, useEffect } from "react";
import { motion } from "framer-motion";
import {
AlertCircle,
Bot,
Brain,
CheckCircle,
Loader2,
RefreshCw,
RotateCcw,
Save,
Settings2,
Zap,
} from "lucide-react";
import { useEffect, useState } from "react";
import { toast } from "sonner";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import {
Select,
SelectContent,
@ -10,23 +27,7 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Brain,
Zap,
Bot,
AlertCircle,
CheckCircle,
Settings2,
RefreshCw,
Save,
RotateCcw,
Loader2,
} from "lucide-react";
import { useLLMConfigs, useLLMPreferences } from "@/hooks/use-llm-configs";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { toast } from "sonner";
const ROLE_DESCRIPTIONS = {
long_context: {
@ -405,7 +406,7 @@ export function LLMRoleManager() {
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Assign LLM Configuration:</label>
<Label className="text-sm font-medium">Assign LLM Configuration:</Label>
<Select
value={currentAssignment?.toString() || "unassigned"}
onValueChange={(value) => handleRoleAssignment(`${key}_llm_id`, value)}
@ -494,7 +495,7 @@ export function LLMRoleManager() {
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<span>Progress:</span>
<div className="flex gap-1">
{Object.keys(ROLE_DESCRIPTIONS).map((key, index) => (
{Object.keys(ROLE_DESCRIPTIONS).map((key) => (
<div
key={key}
className={`w-2 h-2 rounded-full ${

View file

@ -1,10 +1,33 @@
"use client";
import type React from "react";
import { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { AnimatePresence, motion } from "framer-motion";
import {
AlertCircle,
Bot,
CheckCircle,
Clock,
Edit3,
Eye,
EyeOff,
Loader2,
Plus,
RefreshCw,
Settings2,
Trash2,
} from "lucide-react";
import { useEffect, useState } from "react";
import { toast } from "sonner";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Card, CardContent } from "@/components/ui/card";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
@ -14,37 +37,7 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Badge } from "@/components/ui/badge";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import {
Plus,
Trash2,
Bot,
AlertCircle,
Edit3,
Settings2,
Eye,
EyeOff,
CheckCircle,
Clock,
AlertTriangle,
RefreshCw,
Loader2,
} from "lucide-react";
import {
useLLMConfigs,
type CreateLLMConfig,
UpdateLLMConfig,
type LLMConfig,
} from "@/hooks/use-llm-configs";
import { toast } from "sonner";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { type CreateLLMConfig, type LLMConfig, useLLMConfigs } from "@/hooks/use-llm-configs";
const LLM_PROVIDERS = [
{
@ -179,7 +172,7 @@ export function ModelConfigManager() {
setIsSubmitting(true);
let result;
let result: LLMConfig | null = null;
if (editingConfig) {
// Update existing config
result = await updateLLMConfig(editingConfig.id, formData);

View file

@ -1,7 +1,9 @@
"use client";
import { Trash2 } from "lucide-react";
import { useEffect, useState } from "react";
import { AppSidebar } from "@/components/sidebar/app-sidebar";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
@ -10,8 +12,6 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Trash2 } from "lucide-react";
import { apiClient } from "@/lib/api"; // Import the API client
interface Chat {

View file

@ -1,23 +1,23 @@
"use client";
import * as React from "react";
import {
AlertCircle,
BookOpen,
Cable,
ExternalLink,
FileStack,
Undo2,
FileText,
Info,
type LucideIcon,
MessageCircleMore,
Podcast,
Settings2,
SquareLibrary,
SquareTerminal,
AlertCircle,
Info,
ExternalLink,
Trash2,
Podcast,
type LucideIcon,
FileText,
Undo2,
} from "lucide-react";
import { useMemo } from "react";
import { Logo } from "@/components/Logo";
import { NavMain } from "@/components/sidebar/nav-main";
@ -26,7 +26,6 @@ import { NavSecondary } from "@/components/sidebar/nav-secondary";
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
@ -178,7 +177,7 @@ export function AppSidebar({
...props
}: AppSidebarProps) {
// Process navMain to resolve icon names to components
const processedNavMain = React.useMemo(() => {
const processedNavMain = useMemo(() => {
return navMain.map((item) => ({
...item,
icon: iconMap[item.icon] || SquareTerminal, // Fallback to SquareTerminal if icon not found
@ -186,7 +185,7 @@ export function AppSidebar({
}, [navMain]);
// Process navSecondary to resolve icon names to components
const processedNavSecondary = React.useMemo(() => {
const processedNavSecondary = useMemo(() => {
return navSecondary.map((item) => ({
...item,
icon: iconMap[item.icon] || Undo2, // Fallback to Undo2 if icon not found
@ -194,7 +193,7 @@ export function AppSidebar({
}, [navSecondary]);
// Process RecentChats to resolve icon names to components
const processedRecentChats = React.useMemo(() => {
const processedRecentChats = useMemo(() => {
return (
RecentChats?.map((item) => ({
...item,
@ -227,9 +226,6 @@ export function AppSidebar({
{processedRecentChats.length > 0 && <NavProjects chats={processedRecentChats} />}
<NavSecondary items={processedNavSecondary} className="mt-auto" />
</SidebarContent>
{/* <SidebarFooter>
footer
</SidebarFooter> */}
</Sidebar>
);
}

View file

@ -1,7 +1,7 @@
"use client";
import { ExternalLink, Folder, MoreHorizontal, Share, Trash2, type LucideIcon } from "lucide-react";
import { ExternalLink, Folder, type LucideIcon, MoreHorizontal, Share, Trash2 } from "lucide-react";
import { useRouter } from "next/navigation";
import {
DropdownMenu,
DropdownMenuContent,
@ -18,7 +18,6 @@ import {
SidebarMenuItem,
useSidebar,
} from "@/components/ui/sidebar";
import { useRouter } from "next/navigation";
// Map of icon names to their components
const actionIconMap: Record<string, LucideIcon> = {

View file

@ -1,14 +1,14 @@
"use client";
import type * as React from "react";
import type { LucideIcon } from "lucide-react";
import type * as React from "react";
import {
SidebarGroup,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarGroupLabel,
} from "@/components/ui/sidebar";
export function NavSecondary({

View file

@ -1,7 +1,7 @@
"use client";
import { BadgeCheck, ChevronsUpDown, LogOut, Settings } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
DropdownMenu,
@ -18,7 +18,6 @@ import {
SidebarMenuItem,
useSidebar,
} from "@/components/ui/sidebar";
import { useRouter, useParams } from "next/navigation";
export function NavUser({
user,

View file

@ -1,8 +1,7 @@
"use client";
import * as React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import type { ThemeProviderProps } from "next-themes";
import { ThemeProvider as NextThemesProvider } from "next-themes";
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;

View file

@ -1,22 +1,24 @@
"use client";
import * as React from "react";
import { useTheme } from "next-themes";
import { MoonIcon, SunIcon } from "lucide-react";
import { motion } from "framer-motion";
import { MoonIcon, SunIcon } from "lucide-react";
import { useTheme } from "next-themes";
import { useEffect, useState } from "react";
import { Button } from "@/components/ui/button";
export function ThemeTogglerComponent() {
const { theme, setTheme } = useTheme();
const [isClient, setIsClient] = React.useState(false);
const [isClient, setIsClient] = useState(false);
React.useEffect(() => {
useEffect(() => {
setIsClient(true);
}, []);
return (
isClient && (
<button
<Button
variant="ghost"
onClick={() => {
theme === "dark" ? setTheme("light") : setTheme("dark");
}}
@ -63,7 +65,7 @@ export function ThemeTogglerComponent() {
)}
<span className="sr-only">Toggle theme</span>
</button>
</Button>
)
);
}

View file

@ -1,8 +1,8 @@
"use client";
import type * as React from "react";
import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { ChevronDownIcon } from "lucide-react";
import type * as React from "react";
import { cn } from "@/lib/utils";

View file

@ -1,10 +1,9 @@
"use client";
import type * as React from "react";
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
import { cn } from "@/lib/utils";
import type * as React from "react";
import { buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";
function AlertDialog({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;

View file

@ -1,5 +1,5 @@
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import * as React from "react";
import { cn } from "@/lib/utils";
const alertVariants = cva(

View file

@ -1,7 +1,7 @@
"use client";
import type * as React from "react";
import * as AvatarPrimitive from "@radix-ui/react-avatar";
import type * as React from "react";
import { cn } from "@/lib/utils";

View file

@ -1,6 +1,6 @@
import type * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import type * as React from "react";
import { cn } from "@/lib/utils";

View file

@ -1,102 +0,0 @@
import type * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { ChevronRight, MoreHorizontal } from "lucide-react";
import { cn } from "@/lib/utils";
function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />;
}
function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
return (
<ol
data-slot="breadcrumb-list"
className={cn(
"text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5",
className
)}
{...props}
/>
);
}
function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
return (
<li
data-slot="breadcrumb-item"
className={cn("inline-flex items-center gap-1.5", className)}
{...props}
/>
);
}
function BreadcrumbLink({
asChild,
className,
...props
}: React.ComponentProps<"a"> & {
asChild?: boolean;
}) {
const Comp = asChild ? Slot : "a";
return (
<Comp
data-slot="breadcrumb-link"
className={cn("hover:text-foreground transition-colors", className)}
{...props}
/>
);
}
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
return (
<span
data-slot="breadcrumb-page"
role="link"
aria-disabled="true"
aria-current="page"
className={cn("text-foreground font-normal", className)}
{...props}
/>
);
}
function BreadcrumbSeparator({ children, className, ...props }: React.ComponentProps<"li">) {
return (
<li
data-slot="breadcrumb-separator"
role="presentation"
aria-hidden="true"
className={cn("[&>svg]:size-3.5", className)}
{...props}
>
{children ?? <ChevronRight />}
</li>
);
}
function BreadcrumbEllipsis({ className, ...props }: React.ComponentProps<"span">) {
return (
<span
data-slot="breadcrumb-ellipsis"
role="presentation"
aria-hidden="true"
className={cn("flex size-9 items-center justify-center", className)}
{...props}
>
<MoreHorizontal className="size-4" />
<span className="sr-only">More</span>
</span>
);
}
export {
Breadcrumb,
BreadcrumbList,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbPage,
BreadcrumbSeparator,
BreadcrumbEllipsis,
};

View file

@ -1,6 +1,6 @@
import type * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import type * as React from "react";
import { cn } from "@/lib/utils";

View file

@ -1,11 +1,10 @@
"use client";
import * as React from "react";
import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
import * as React from "react";
import { type DayButton, DayPicker, getDefaultClassNames } from "react-day-picker";
import { cn } from "@/lib/utils";
import { Button, buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";
function Calendar({
className,

View file

@ -1,8 +1,8 @@
"use client";
import type * as React from "react";
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
import { CheckIcon } from "lucide-react";
import type * as React from "react";
import { cn } from "@/lib/utils";

View file

@ -1,8 +1,8 @@
"use client";
import * as React from "react";
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { X } from "lucide-react";
import * as React from "react";
import { cn } from "@/lib/utils";

View file

@ -1,7 +1,7 @@
"use client";
import { cn } from "@/lib/utils";
import { Sparkles } from "lucide-react";
import { cn } from "@/lib/utils";
interface DisplayCardProps {
className?: string;

View file

@ -1,8 +1,8 @@
"use client";
import type * as React from "react";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
import type * as React from "react";
import { cn } from "@/lib/utils";

View file

@ -1,8 +1,8 @@
"use client";
import * as React from "react";
import type * as LabelPrimitive from "@radix-ui/react-label";
import { Slot } from "@radix-ui/react-slot";
import * as React from "react";
import {
Controller,
type ControllerProps,
@ -12,9 +12,8 @@ import {
useFormContext,
useFormState,
} from "react-hook-form";
import { cn } from "@/lib/utils";
import { Label } from "@/components/ui/label";
import { cn } from "@/lib/utils";
const Form = FormProvider;

View file

@ -1,56 +0,0 @@
"use client";
import { Label } from "@/components/ui/label";
import { type Tag, TagInput } from "emblor";
import { useState } from "react";
const tags = [
{
id: "1",
text: "Red",
},
];
function InputDemo() {
const [exampleTags, setExampleTags] = useState<Tag[]>(tags);
const [activeTagIndex, setActiveTagIndex] = useState<number | null>(null);
return (
<div className="space-y-2 w-[300px]">
<Label htmlFor="input-57">Input with inner tags</Label>
<TagInput
id="input-57"
tags={exampleTags}
setTags={(newTags) => {
setExampleTags(newTags);
}}
placeholder="Add a tag"
styleClasses={{
inlineTagsContainer:
"border-input rounded-lg bg-background shadow-sm shadow-black/5 transition-shadow focus-within:border-ring focus-within:outline-none focus-within:ring-[3px] focus-within:ring-ring/20 p-1 gap-1",
input: "w-full min-w-[80px] focus-visible:outline-none shadow-none px-2 h-7",
tag: {
body: "h-7 relative bg-background border border-input hover:bg-background rounded-md font-medium text-xs ps-2 pe-7 flex",
closeButton:
"absolute -inset-y-px -end-px p-0 rounded-e-lg flex size-7 transition-colors outline-0 focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring/70 text-muted-foreground/80 hover:text-foreground",
},
}}
activeTagIndex={activeTagIndex}
setActiveTagIndex={setActiveTagIndex}
/>
<p className="mt-2 text-xs text-muted-foreground" role="region" aria-live="polite">
Built with{" "}
<a
className="underline hover:text-foreground"
href="https://github.com/JaleelB/emblor"
target="_blank"
rel="noopener nofollow"
>
emblor
</a>
</p>
</div>
);
}
export { InputDemo };

View file

@ -1,7 +1,7 @@
"use client";
import type * as React from "react";
import * as LabelPrimitive from "@radix-ui/react-label";
import type * as React from "react";
import { cn } from "@/lib/utils";

View file

@ -1,13 +1,11 @@
import type * as React from "react";
import { ChevronLeftIcon, ChevronRightIcon, MoreHorizontalIcon } from "lucide-react";
import { cn } from "@/lib/utils";
import type * as React from "react";
import { type Button, buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";
function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
return (
<nav
role="navigation"
aria-label="pagination"
data-slot="pagination"
className={cn("mx-auto flex w-full justify-center", className)}

View file

@ -1,7 +1,7 @@
"use client";
import type * as React from "react";
import * as PopoverPrimitive from "@radix-ui/react-popover";
import type * as React from "react";
import { cn } from "@/lib/utils";

View file

@ -1,8 +1,8 @@
"use client";
import type * as React from "react";
import * as SelectPrimitive from "@radix-ui/react-select";
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
import type * as React from "react";
import { cn } from "@/lib/utils";

View file

@ -1,7 +1,7 @@
"use client";
import type * as React from "react";
import * as SeparatorPrimitive from "@radix-ui/react-separator";
import type * as React from "react";
import { cn } from "@/lib/utils";

View file

@ -1,8 +1,8 @@
"use client";
import type * as React from "react";
import * as SheetPrimitive from "@radix-ui/react-dialog";
import { XIcon } from "lucide-react";
import type * as React from "react";
import { cn } from "@/lib/utils";

View file

@ -1,12 +1,9 @@
"use client";
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { type VariantProps, cva } from "class-variance-authority";
import { cva, type VariantProps } from "class-variance-authority";
import { PanelLeftIcon } from "lucide-react";
import { useIsMobile } from "@/hooks/use-mobile";
import { cn } from "@/lib/utils";
import * as React from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
@ -19,6 +16,8 @@ import {
} from "@/components/ui/sheet";
import { Skeleton } from "@/components/ui/skeleton";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { useIsMobile } from "@/hooks/use-mobile";
import { cn } from "@/lib/utils";
const SIDEBAR_COOKIE_NAME = "sidebar_state";
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
@ -86,7 +85,7 @@ function SidebarProvider({
// Helper to toggle the sidebar.
const toggleSidebar = React.useCallback(() => {
return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);
}, [isMobile, setOpen, setOpenMobile]);
}, [isMobile, setOpen]);
// Adds a keyboard shortcut to toggle the sidebar.
React.useEffect(() => {
@ -115,7 +114,7 @@ function SidebarProvider({
setOpenMobile,
toggleSidebar,
}),
[state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
[state, open, setOpen, isMobile, openMobile, toggleSidebar]
);
return (

View file

@ -1,7 +1,7 @@
"use client";
import * as React from "react";
import * as SliderPrimitive from "@radix-ui/react-slider";
import * as React from "react";
import { cn } from "@/lib/utils";

View file

@ -1,6 +1,6 @@
"use client";
import React, { useRef, useState, useCallback, useEffect } from "react";
import { motion, useSpring, useTransform, type SpringOptions } from "framer-motion";
import { motion, type SpringOptions, useSpring, useTransform } from "framer-motion";
import { useCallback, useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
type SpotlightProps = {

View file

@ -1,7 +1,7 @@
"use client";
import * as React from "react";
import * as TabsPrimitive from "@radix-ui/react-tabs";
import * as React from "react";
import { cn } from "@/lib/utils";

View file

@ -1,16 +1,16 @@
"use client";
import type React from "react";
import { useRef } from "react";
import {
type MotionStyle,
motion,
type SpringOptions,
useMotionTemplate,
useMotionValue,
useSpring,
useTransform,
type MotionStyle,
type SpringOptions,
} from "framer-motion";
import type React from "react";
import { useRef } from "react";
type TiltProps = {
children: React.ReactNode;

View file

@ -1,7 +1,7 @@
"use client";
import type * as React from "react";
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import type * as React from "react";
import { cn } from "@/lib/utils";