Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@
"@langchain/core": "^0.3.3",
"@nestjs/common": "^10.4.6",
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.4",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-scroll-area": "^1.2.0",
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.2",
"@radix-ui/react-separator": "^1.1.1",
"@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-tooltip": "^1.1.6",
"@types/dom-speech-recognition": "^0.0.4",
"class-variance-authority": "^0.7.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"emoji-mart": "^5.6.0",
"framer-motion": "^11.5.6",
Expand Down
109 changes: 59 additions & 50 deletions frontend/src/app/(main)/MainLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
'use client';

import React, { useEffect, useState } from 'react';
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from '@/components/ui/resizable';
import { cn } from '@/lib/utils';
import { usePathname } from 'next/navigation';
import Sidebar from '@/components/sidebar';
import { useChatList } from '../hooks/useChatList';
import { ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable';
import CustomSidebar from '@/components/sidebar';
import { SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar';

export default function MainLayout({
children,
Expand All @@ -20,10 +17,6 @@ export default function MainLayout({
const [isMobile, setIsMobile] = useState(false);
const defaultLayout = [30, 160];
const navCollapsedSize = 10;

const pathname = usePathname();
const currentChatId = pathname.split('/')[1] || '';

const {
chats,
loading,
Expand All @@ -33,6 +26,10 @@ export default function MainLayout({
refetchChats,
} = useChatList();

useEffect(() => {
document.cookie = `react-resizable-panels:collapsed=${JSON.stringify(isCollapsed)}; path=/; max-age=604800`;
}, [isCollapsed]);

useEffect(() => {
const checkScreenWidth = () => {
setIsMobile(window.innerWidth <= 1023);
Expand All @@ -44,54 +41,66 @@ export default function MainLayout({
};
}, []);

console.log(`${isCollapsed}, ${isMobile}`);

return (
<main className="flex h-[calc(100dvh)] flex-col items-center">
<ResizablePanelGroup
direction="horizontal"
onLayout={(sizes: number[]) => {
document.cookie = `react-resizable-panels:layout=${JSON.stringify(sizes)}`;
document.cookie = `react-resizable-panels:layout=${JSON.stringify(
sizes
)}; path=/; max-age=604800`;
}}
className="h-screen items-stretch"
>
<ResizablePanel
defaultSize={defaultLayout[0]}
collapsedSize={navCollapsedSize}
collapsible={true}
minSize={isMobile ? 0 : 12}
maxSize={isMobile ? 0 : 16}
onCollapse={() => {
setIsCollapsed(true);
document.cookie = `react-resizable-panels:collapsed=${JSON.stringify(true)}`;
}}
onExpand={() => {
setIsCollapsed(false);
document.cookie = `react-resizable-panels:collapsed=${JSON.stringify(false)}`;
}}
className={cn(
isCollapsed
? 'min-w-[50px] md:min-w-[70px] transition-all duration-300 ease-in-out'
: 'hidden md:block'
)}
>
<Sidebar
isCollapsed={isCollapsed}
isMobile={isMobile}
currentChatId={currentChatId}
chatListUpdated={chatListUpdated}
setChatListUpdated={setChatListUpdated}
chats={chats}
loading={loading}
error={error}
onRefetch={refetchChats}
/>
</ResizablePanel>
<ResizableHandle className={cn('hidden md:flex')} withHandle />
<ResizablePanel
className="h-full w-full flex justify-center"
defaultSize={defaultLayout[1]}
>
{children}
</ResizablePanel>
<SidebarProvider>
<ResizablePanel
defaultSize={defaultLayout[0]}
collapsedSize={navCollapsedSize}
collapsible={true}
minSize={isMobile ? 4 : 12}
maxSize={isMobile ? 10 : 16}
onCollapse={() => {
console.log(`setting collapse to T`);
setIsCollapsed(true);
}}
onExpand={() => {
console.log(`setting collapse to F`);
setIsCollapsed(false);
}}
className={cn(
'transition-all duration-300 ease-in-out',
isCollapsed ? 'min-w-[50px] md:min-w-[70px]' : 'md:min-w-[200px]'
)}
>
{loading ? (
<div className="flex justify-center items-center">Loading...</div>
) : error ? (
<div className="flex justify-center items-center text-red-500">
Error: {error.message}
</div>
) : (
<CustomSidebar
isCollapsed={isCollapsed}
isMobile={isMobile}
chatListUpdated={chatListUpdated}
setChatListUpdated={setChatListUpdated}
chats={chats}
loading={loading}
error={error}
onRefetch={refetchChats}
/>
)}
</ResizablePanel>

<ResizablePanel
className="h-full w-full flex justify-center"
defaultSize={defaultLayout[1]}
>
{children}
</ResizablePanel>
</SidebarProvider>
</ResizablePanelGroup>
</main>
);
Expand Down
1 change: 1 addition & 0 deletions frontend/src/app/(main)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Metadata, Viewport } from 'next';
import { Inter } from 'next/font/google';
import MainLayout from './MainLayout';
import { SidebarProvider } from '@/components/ui/sidebar';

const inter = Inter({ subsets: ['latin'] });

Expand Down
16 changes: 16 additions & 0 deletions frontend/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@
--input: 240 5.9% 90%;
--ring: 8 100% 60%;
--radius: 1rem;
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 240 5.9% 10%;
--sidebar-primary-foreground: 0 0% 98%;
--sidebar-accent: 240 4.8% 95.9%;
--sidebar-accent-foreground: 240 5.9% 10%;
--sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%;
}

.dark {
Expand All @@ -46,6 +54,14 @@
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--ring: 8 90% 55%;
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 224.3 76.3% 48%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
}

Expand Down
41 changes: 41 additions & 0 deletions frontend/src/components/detail-settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { useEffect } from 'react';
import { Button } from './ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from './ui/dialog';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';

import { DownloadIcon, GearIcon } from '@radix-ui/react-icons';
import PullModelForm from './pull-model-form';
import EditUsernameForm from './edit-username-form';

export default function DetailSettings() {
const [isOpen, setIsOpen] = React.useState(false);
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger className="w-full">
<div className="flex w-full gap-2 p-1 items-center cursor-pointer">
<GearIcon className="w-4 h-4" />
Settings
</div>
</DialogTrigger>
<DialogContent>
<DialogHeader className="space-y-4">
<DialogTitle>Settings</DialogTitle>
<EditUsernameForm setOpen={setIsOpen} />
</DialogHeader>
</DialogContent>
</Dialog>
);
}
1 change: 1 addition & 0 deletions frontend/src/components/pull-model-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export default function PullModelForm() {
setName(e.currentTarget.value);
};

console.log('enter model');
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="w-full space-y-6">
Expand Down
9 changes: 7 additions & 2 deletions frontend/src/components/pull-model.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,20 @@ import { DownloadIcon } from '@radix-ui/react-icons';
import PullModelForm from './pull-model-form';

export default function PullModel() {
const [isOpen, setIsOpen] = React.useState(false);
return (
<Dialog>
<DialogTrigger asChild>
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger>
<div className="flex w-full gap-2 p-1 items-center cursor-pointer">
<DownloadIcon className="w-4 h-4" />
<p>Pull model</p>
</div>
</DialogTrigger>

<DialogContent className="space-y-2">
<DialogDescription>
Specify the model you want to pull and download to your device.
</DialogDescription>
<DialogTitle>Pull Model</DialogTitle>
<PullModelForm />
</DialogContent>
Expand Down
36 changes: 26 additions & 10 deletions frontend/src/components/sidebar-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,38 +20,54 @@ import { useMutation } from '@apollo/client';
import { MoreHorizontal, Trash2 } from 'lucide-react';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { toast } from 'sonner';
import { EventEnum } from './enum';

interface SideBarItemProps {
id: string;
currentChatId: string;
title: string;
isSelected: boolean;
onSelect: (id: string) => void;
refetchChats: () => void;
}

export function SideBarItem({
id,
currentChatId,
title,
isSelected,
onSelect,
refetchChats,
}: SideBarItemProps) {
const router = useRouter();
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [isSelected, setIsSelected] = useState(false);
const [variant, setVariant] = useState<
'ghost' | 'link' | 'secondary' | 'default' | 'destructive' | 'outline'
>('ghost');

useEffect(() => {
const selected = currentChatId === id;
setIsSelected(selected);
if (selected) {
setVariant('secondary'); // 类型安全
} else {
setVariant('ghost'); // 类型安全
}
refetchChats();
console.log(`update sidebar ${currentChatId}`);
}, [currentChatId]);

const [deleteChat] = useMutation(DELETE_CHAT, {
onCompleted: () => {
toast.success('Chat deleted successfully');
refetchChats();
console.log(`${id} ${isSelected}`);
if (isSelected) {
window.history.pushState({}, '', '/');
window.history.replaceState({}, '', '/');
const event = new Event(EventEnum.NEW_CHAT);
window.dispatchEvent(event);
}
refetchChats();
},
onError: (error) => {
console.error('Error deleting chat:', error);
Expand Down Expand Up @@ -83,14 +99,14 @@ export function SideBarItem({
<div
className={cn(
buttonVariants({
variant: isSelected ? 'secondaryLink' : 'ghost',
variant,
}),
'flex justify-between w-full h-14 text-base font-normal items-center group'
)}
>
<Link
href={`/?id=${id}`}
className="flex-1 flex gap-3 items-center truncate"
className="flex-1 flex gap-3 items-center truncate ml-2"
onClick={handleChatClick}
>
<div className="flex flex-col">
Expand All @@ -103,7 +119,7 @@ export function SideBarItem({
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="flex justify-end items-center dropdown-trigger"
className="flex justify-end items-center dropdown-trigger mr-2"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
Expand All @@ -122,7 +138,7 @@ export function SideBarItem({
>
<Button
variant="ghost"
className="w-full flex gap-2 hover:text-red-500 text-red-500 justify-start items-center"
className="w-full flex hover:text-red-500 text-red-500 justify-start items-center"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
Expand Down
Loading
Loading