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,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";