mirror of
https://github.com/supabase/supabase.git
synced 2026-09-06 09:59:03 +08:00
<img width="1693" height="1038" alt="image" src="https://github.com/user-attachments/assets/9ef52446-c517-4ea9-ada6-ac6dc57c8af4" /> ## Summary - add an Explorer-specific chat toolbar with rename, chat ID, permission, and branching controls - list searchable non-support chats in Explorer with reactive updates and safe rehydrated-date sorting - add chat creation entry points to Explorer home and navigation menus - route chat recent items correctly and show chat icons across shared tab surfaces - make the assistant sidebar expand action open the active chat in Explorer This is PR 3 of 3 and is stacked on #49031. Review #48973 first, then #49031, then this PR. Compared with its base, this PR contains only discovery, toolbar, and cross-surface integration work. ## To Test - visit /explorer - Create a new chat either via home tab chat input OR the chats sidebar - Validate chats show up in sidebar - Validate chat conversation works - Close the chat tab and open a chat via sidebar - Change permission settings via the chat tab toolbar and verify it persists ## Test plan - `mise exec node@22 -- pnpm --dir apps/studio exec tsc --noEmit` - focused Vitest suite: 4 files / 5 tests covering reactive chat lists, navigation filtering and sorting, recent-item routing, and sidebar handoff - ESLint on changed TypeScript files - Prettier check on changed source files <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added chat creation from the Explorer home, navigation, and new-tab menu. - Added an Explorer chat toolbar with editable names, ID copying, permissions, shortcuts, and metadata warnings. - Added searchable chat history with sorting, active-chat highlighting, and clear empty states. - Chats can now open directly in Explorer from the AI Assistant panel. - Recent items now link correctly to chats and notebooks. - **Bug Fixes** - Improved chat list updates after creation, deletion, and restored sessions. - Added safer handling for chats without update timestamps. - **Tests** - Expanded coverage for chat navigation, creation, metadata warnings, shortcuts, and recent-item links. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
106 lines
3.2 KiB
TypeScript
106 lines
3.2 KiB
TypeScript
import { motion } from 'framer-motion'
|
|
import { ChevronLeft, MessageSquare, NotebookText, Plus } from 'lucide-react'
|
|
import { type ComponentType, type PropsWithChildren } from 'react'
|
|
import { Button, cn } from 'ui'
|
|
import { InnerSideBarFilters, InnerSideBarFilterSearchInput } from 'ui-patterns/InnerSideMenu'
|
|
|
|
import { useCreateChat, useCreateNotebook } from '@/components/interfaces/Explorer/hooks'
|
|
import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
|
|
|
|
export type ExplorerResourceType = 'notebook' | 'chat'
|
|
|
|
export const LEVEL_OFFSET = 8
|
|
export const LEVEL_TRANSITION = { duration: 0.09, ease: 'easeOut' } as const
|
|
|
|
export const EXPLORER_SECTIONS: Array<{
|
|
type: ExplorerResourceType
|
|
label: string
|
|
icon: ComponentType<{ size?: number; className?: string }>
|
|
searchPlaceholder: string
|
|
}> = [
|
|
{
|
|
type: 'notebook',
|
|
label: 'Notebooks',
|
|
icon: NotebookText,
|
|
searchPlaceholder: 'Search notebooks',
|
|
},
|
|
{ type: 'chat', label: 'Chats', icon: MessageSquare, searchPlaceholder: 'Search chats' },
|
|
]
|
|
|
|
export const rowClassName = (isActive: boolean) =>
|
|
cn(
|
|
'group relative flex h-7 w-full items-center gap-2 rounded-md pl-3 pr-2 text-sm',
|
|
isActive
|
|
? 'bg-selection text-foreground'
|
|
: 'text-foreground-light hover:bg-surface-200 hover:text-foreground'
|
|
)
|
|
|
|
export const ExplorerNavResourceWrapper = ({
|
|
type,
|
|
label,
|
|
className,
|
|
children,
|
|
search,
|
|
setSearch,
|
|
onBack,
|
|
}: PropsWithChildren<{
|
|
type: ExplorerResourceType
|
|
label?: string
|
|
className?: string
|
|
search?: string
|
|
setSearch: (value: string) => void
|
|
onBack: () => void
|
|
}>) => {
|
|
const { createNotebook } = useCreateNotebook()
|
|
const { createChat } = useCreateChat()
|
|
const searchPlaceholder = EXPLORER_SECTIONS.find((x) => x.type === type)?.searchPlaceholder
|
|
|
|
return (
|
|
<motion.div
|
|
role="group"
|
|
aria-label={label}
|
|
initial={{ opacity: 0, x: LEVEL_OFFSET }}
|
|
animate={{ opacity: 1, x: 0 }}
|
|
exit={{ opacity: 0, x: LEVEL_OFFSET }}
|
|
transition={LEVEL_TRANSITION}
|
|
className={cn('absolute inset-0 flex flex-col', className)}
|
|
>
|
|
<div className="flex items-center gap-2 p-3 pb-2">
|
|
<Button
|
|
size="tiny"
|
|
variant="outline"
|
|
aria-label="Back"
|
|
onClick={onBack}
|
|
className="size-7 shrink-0 px-0"
|
|
icon={<ChevronLeft />}
|
|
/>
|
|
<span id="explorer-sidebar-search-label" className="sr-only">
|
|
{searchPlaceholder}
|
|
</span>
|
|
<InnerSideBarFilters className="w-full gap-0 p-0">
|
|
<InnerSideBarFilterSearchInput
|
|
name="explorer-sidebar-search"
|
|
value={search}
|
|
placeholder={searchPlaceholder}
|
|
aria-labelledby="explorer-sidebar-search-label"
|
|
onChange={(event) => setSearch(event.target.value)}
|
|
/>
|
|
</InnerSideBarFilters>
|
|
<ButtonTooltip
|
|
size="tiny"
|
|
variant="outline"
|
|
aria-label={`New ${type}`}
|
|
className="size-7 shrink-0 px-0"
|
|
icon={<Plus />}
|
|
tooltip={{ content: { side: 'bottom', text: `New ${type}` } }}
|
|
onClick={() => {
|
|
if (type === 'notebook') createNotebook()
|
|
if (type === 'chat') createChat()
|
|
}}
|
|
/>
|
|
</div>
|
|
{children}
|
|
</motion.div>
|
|
)
|
|
}
|