mirror of
https://github.com/MODSetter/SurfSense.git
synced 2025-09-01 18:19:08 +00:00
Biome: fixes for compontents directory
This commit is contained in:
parent
758603b275
commit
2950573271
69 changed files with 478 additions and 648 deletions
|
@ -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>
|
||||
|
|
|
@ -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 }) => {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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 */}
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
})
|
||||
)}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ">
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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 && (
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
)}
|
||||
|
|
|
@ -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({});
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { Source, Connector } from "./types";
|
||||
import type { Connector, Source } from "./types";
|
||||
|
||||
/**
|
||||
* Function to get sources for the main view
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -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 ${
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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 ${
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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> = {
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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} />;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 };
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue